import expressions from 'angular-expressions';
import { diyFunction } from '@/views/legal/process/components/formMake/prop/formulaEdit/functions.js';
import { forEachFormOptions, isNumberType } from './formMakeUtils';
import mathFun from '@/utils/math';

// const devConsole = (msg, type = 'log') => {
//   if (process.env.NODE_ENV === 'development') {
//     console[type](...msg);
//   }
// };
const getValueScopedKey = (key) => `$_widget_${key}_end$`;
const getValueScopedParentKey = (key, parentKey, index) => `$_widget_${key}_${parentKey}_${index}_end$`;
// 初始化所有高级公式字段
export function initMathFormulaFunction(formOptions, modifyElement = null) {
  if (!formOptions || !formOptions.list) return;
  if (formOptions._initDetail) {
    formOptions._initDetail = false;
    // devConsole(['initDetail', formOptions._initDetail]);
    return;
  }
  // devConsole(['initMathFormulaFunction：', modifyElement && modifyElement.name]);
  const formulaHighMode = formOptions.config.formulaHighMode;
  // 避免无必要的计算，只有被引用到的字段修改了，才会触发计算
  if (formulaHighMode && modifyElement && !['formulaInput', 'subform', 'linkForm'].includes(modifyElement.type) && !formulaHighMode.includes(modifyElement.key)) {
    // console.log('reject math');
    return;
  }
  const hightNotSubTableFormula = [];
  const hightSubTableFormula = [];
  const valueScoped = {};
  const isComplated = [];
  const allFieldMap = new Map();
  /** 功能
    1、收集引用了动态字段的公式
    2、收集值域
    3、计算常量公式（即没有引用其他动态字段的公式）
   */
  forEachFormOptions(formOptions.list, (element, subformKey, parentSubform) => {
    allFieldMap.set(element.key, element);
    if (element.type === 'formulaInput' && element.options.modeType === 1 && element.options.formulaString) {
      if (element.options.formulaString.indexOf('$_widget_') < 0) {
        // 先计算没有动态字段的值
        const value = mathFormulaFunction(element.options.formulaString);
        isComplated.push(element.key);
        setValue(element, value, parentSubform);
      } else {
        // 把有引入其他动态字段的公式先存起来
        if (element.isSubTable) {
          hightSubTableFormula.push(element);
        } else {
          hightNotSubTableFormula.push(element);
        }
      }
    }
    /**
     * 获取值域
     */
    if (!['grid', 'title', 'titleExplain'].includes(element.type)) {
      // const dynamicFormula = element.type === 'formulaInput' && element.options.modeType === 1 && element.options.formulaString && element.options.formulaString.indexOf('$_widget_') >= 0;
      // if (!dynamicFormula) {
      if (element.type === 'linkForm' && element.options.isStatistics && element.options.statistics) {
        setLinkFormStatistics(element, valueScoped);
      }
      if (!element.isSubTable) {
        valueScoped[getValueScopedKey(element.key)] = getValue(element);
      } else if (parentSubform && parentSubform.value) {
        const subTableValue = [];
        parentSubform.value.forEach((i, index) => {
          const val = getValue(element, i[element.key]);
          subTableValue.push(val);
          valueScoped[getValueScopedParentKey(element.key, parentSubform.key, index)] = val;
        });
        valueScoped[getValueScopedKey(element.key)] = subTableValue;
      } else if (parentSubform) {
        valueScoped[getValueScopedKey(element.key)] = undefined;
      } else {
        valueScoped[getValueScopedKey(element.key)] = getValue(element);
      }
      // }
    }
  });
  const hightFormula = hightNotSubTableFormula;
  hightSubTableFormula.forEach(el => {
    chunkField(el, allFieldMap, hightFormula);
  });
  hightFormula.forEach((item) => {
    if (!isComplated.includes(item.key)) {
      const data = deepMath(item, [], { allFieldMap, valueScoped, isComplated });
      const _parentSubform = allFieldMap.get(item.linkFormKey);
      setValue(item, data, _parentSubform);
    }
  });
}

function deepMath(curentElement, cache, { allFieldMap, valueScoped, isComplated }) {
  if (isComplated.includes(curentElement.key)) {
    return valueScoped[getValueScopedKey(curentElement.key)];
  }
  if (cache.includes(curentElement.key)) {
    return undefined;
  }
  const linkField = getFormulaField(curentElement, allFieldMap);
  cache.push(curentElement.key);
  while (linkField.length > 0) {
    const ele = linkField.shift();
    if (ele.type === 'formulaInput' && ele.options.modeType === 1) {
      const hightFormula = [];
      chunkField(ele, allFieldMap, hightFormula);
      hightFormula.forEach((item) => {
        if (!isComplated.includes(item.key)) {
          const data = deepMath(item, cache, { allFieldMap, valueScoped, isComplated });
          const _parentSubform = allFieldMap.get(item.linkFormKey);
          setValue(item, data, _parentSubform);
        }
      });
    }
  }

  if (linkField.length === 0) {
    try {
      const formulaString = curentElement.options.formulaString;
      // devConsole([curentElement.name, formulaString, _.cloneDeep(valueScoped)]);
      const data = mathFormulaFunction(formulaString, valueScoped);
      isComplated.push(curentElement.key);
      // 如果整个分块全都完成了，则标记原公式字段为完成
      if (curentElement._chunkOptions && curentElement._chunkOptions._chunkEnd) {
        isComplated.push(curentElement._chunkOptions._originKey);
      }
      if (data instanceof Error) {
        return undefined;
      } else {
        if (curentElement._chunkOptions) {
          const _chunkOptions = curentElement._chunkOptions;
          const _parentSubform = allFieldMap.get(curentElement.linkFormKey);
          const k = getValueScopedParentKey(_chunkOptions._originKey, _parentSubform.key, _chunkOptions._chunk);
          // 更新分块的值域 $_widget_${key}_${parentKey}_${index}_end$
          valueScoped[k] = data;
          // 更新当前节点所在的子表单的值
          const subTableValue = [];
          valueScoped[getValueScopedKey(_parentSubform.key)].forEach((j, jindex) => {
            if (jindex === _chunkOptions._chunk) {
              j[_chunkOptions._originKey] = data;
            }
            subTableValue.push(j[_chunkOptions._originKey]);
          });
          valueScoped[getValueScopedKey(_chunkOptions._originKey)] = subTableValue;
        } else if (curentElement.isSubTable) {
          const _parentSubform = allFieldMap.get(curentElement.linkFormKey);
          // 更新分块的值域 $_widget_${key}_${parentKey}_${index}_end$
          const subTableValue = [];
          _parentSubform.value.forEach((i, index) => {
            subTableValue.push(data);
            valueScoped[getValueScopedParentKey(curentElement.key, _parentSubform.key, index)] = data;
          });
          valueScoped[getValueScopedKey(curentElement.key)] = subTableValue;
        } else {
          valueScoped[getValueScopedKey(curentElement.key)] = data;
        }
        return data;
      }
    } catch (error) {
      return undefined;
    }
  }
}
// 获取高级公式字段中引用到的动态字段
function getFormulaField(element, allFieldMap) {
  const formulaString = element.options.formulaString;
  if (!formulaString || formulaString.indexOf('$_widget_') < 0) return [];
  const widget = /\$_widget_(.+?)_end\$/g;
  const ansField = [];
  const ansFieldKey = [];
  formulaString.replace(widget, (math, key) => {
    if (allFieldMap.has(key) && !ansFieldKey.includes(key)) {
      ansField.push(allFieldMap.get(key));
      ansFieldKey.push(key);
    }
  });
  return ansField;
}
// 将子表单中的公式字段分块
function chunkField(element, allFieldMap, result = []) {
  const formulaString = element.options.formulaString;
  const _parentSubform = allFieldMap.get(element.linkFormKey);
  if (formulaString && _parentSubform && _parentSubform.value) {
    const allLinkField = getFormulaField(element, allFieldMap);
    const parentKeys = _parentSubform.subform.map(s => s.key);
    const hasSameEl = allLinkField.some(link => {
      return parentKeys.includes(link.key);
    });
    if (hasSameEl) {
      const widget = /\$_widget_(.+?)_end\$/g;
      _parentSubform.value.forEach((pv, index) => {
        const cloneFormulaString = formulaString.replace(widget, function(math, key) {
          if (parentKeys.includes(key)) {
            allFieldMap.set(`${key}_${_parentSubform.key}_${index}`, allFieldMap.get(key));
            return getValueScopedParentKey(key, _parentSubform.key, index);
          }
          return math;
        });
        const cloneEl = _.cloneDeep(element);
        const _chunkOptions = {
          _chunk: index,
          _originKey: cloneEl.key,
          _chunkStart: index === 0,
          _chunkEnd: index === _parentSubform.value.length - 1
        };
        cloneEl._chunkOptions = _chunkOptions;
        cloneEl.key = cloneEl.key + '-' + index;
        cloneEl.options.formulaString = cloneFormulaString;
        result.push(cloneEl);
        allFieldMap.set(cloneEl.key, cloneEl);
      });
    } else {
      result.push(element);
    }
  } else {
    result.push(element);
  }
}
// 设置值
function setValue(element, value, parentSubform) {
  if (value instanceof Error) {
    value = undefined;
  }
  element.value = value;
  if (element.isSubTable && parentSubform && parentSubform.value) {
    const _chunkOptions = element._chunkOptions;
    if (_chunkOptions) {
      parentSubform.value[_chunkOptions._chunk][_chunkOptions._originKey] = value;
    } else {
      parentSubform.value.forEach(i => {
        i[element.key] = value;
      });
    }
  }
}

// 计算函数式公式字段
export const mathFormulaFunction = function(formulaString, valueScoped = {}) {
  try {
    const scoped = { ...diyFunction, ..._.cloneDeep(valueScoped) };
    validateToken(formulaString, scoped);
    const compile = expressions.compile(formulaString);
    let data = compile(scoped);
    if (data === undefined || (typeof data === 'number' && isNaN(data)) || data === null || typeof data === 'function') {
      return new Error('formula error');
    }
    if (typeof data === 'number' && data.toString().length >= 12) {
      data = Number(data.toFixed(4));
    }
    return data;
  } catch (error) {
    return new Error(error);
  }
};

/**
 * 分析公式字符串，生成token，然后在判断 token里面变量部分是否存在于作用域内，不存在则报错
 * @param {*} text 公式字符串
 * @param {*} scope 作用域
 */
function validateToken(text, scope = {}) {
  // const widget = /\$_widget_(.+?)_end\$/g;
  // text.replace(widget, function(math, key) {
  //   if (scope[math] === undefined || scope[math] === null) {
  //     throw new Error(math + '变量不存在');
  //   }
  // });
  const lexer = new expressions.Lexer({});
  const token = lexer.lex(text);
  if (token && token.length) {
    token.forEach(i => {
      if (i.identifier && i.text.indexOf('$_widget_') >= 0 && (scope[i.text] === undefined || scope[i.text] === null)) {
        throw new Error(i.text + '变量不存在');
      }
    });
  }
}

/**
 * 根据字段的类型对值进行处理
 * @param {*} element
 */
function getValue(element, subTableValue) {
  const value = element.isSubTable ? subTableValue : element.value;
  if (isNumberType(element) && !value && value !== 0) return undefined;
  if (!value) return value;
  if (Array.isArray(value) && value.length === 0) return '';
  if (isNumberType(element) || element.type === 'date') {
    if (element.type === 'input' && element.options.isShowPercentage) return mathFun.divide(Number(value), 100);
    return Number(value);
  } else if (['user', 'post', 'department'].includes(element.type)) {
    const labels = {
      user: 'userName',
      post: 'name',
      department: 'name'
    };
    const isArray = Array.isArray(value);
    return isArray ? value.map(i => i[labels[element.type]]) : value[labels[element.type]];
  } else if (element.type === 'address') {
    return value.join('');
  } else if (element.type === 'upAttachment') {
    const isArray = Array.isArray(value);
    return isArray ? value.map(i => i.name) : '';
  }
  return value;
}

function setLinkFormStatistics(el, result) {
  const element = _.cloneDeep(el);
  const listData = element.value;
  let unit = '元';
  if (element.type === 'linkForm' && element.options.remoteParams && element.options.remoteParams.isCustom === false) {
    unit = '分';
  }
  element.options.statistics.forEach(key => {
    const item = element.subform.find(subItem => subItem.key === key);
    let ans;
    listData.forEach(j => {
      let data = element.options.remoteParams.isCustom ? j.content[item.key] : j[item.key];
      if (data || data === 0) {
        data = unit === '分' ? Number(data) / 100 : Number(data);
        ans = ans === undefined ? Number(data) : mathFun.add(ans, Number(data));
      }
    });
    result[getValueScopedKey(`${element.key}_${key}`)] = ans;
  });
}
